'engine.bas
#include once "fbgfx.bi"
#include once "mud-gui/ui.bi"
#include once "mud-gui/event.bi"
#include once "mud-gui/widgets/basewidget.bi"
#include once "mud-gui/util.bi"

namespace ui

	extern _MainWidget_ as BaseWidget ptr

	dim shared _MainWidget_ as BaseWidget ptr

	dim shared as custom_bg_func custombgfunc
	dim shared as any ptr custombgdata

	type uitimer
		w as BaseWidget ptr
		t as double
		i as TIMER_E
	end type

	type uitimer_list
		_data as uitimer ptr
		_next as uitimer_list ptr
		declare destructor()
	end type

	declare sub _timer_( byval t as uitimer ptr )
	declare function _focus_( byval w as BaseWidget ptr =0, byval flag as integer = 0 ) as BaseWidget ptr
	declare function _main_loop_( byval w as BaseWidget ptr = 0 ) as integer
	declare function _process_events_(  ) as _GUI_EVENT_ ptr

	destructor uitimer_list()
		if _data <> 0 then delete _data
		if _next <> 0 then delete _next
	end destructor

	sub setCustomBackgroundProvider( byval _x_ as custom_bg_func, byval _data_ as any ptr )

		custombgfunc = _x_
		custombgdata = _data_

	end sub

	function setFocus( byval widget as BaseWidget ptr ) as BaseWidget ptr
		return _focus_(widget)
	End function

	function RunGui( byval mw as BaseWidget ptr ) as integer

		_MainWidget_ = mw
		var lastTime = timer
		dim as uinteger fps
		dim as uinteger favg = 0
		dim as uinteger fnum = 0

		while 1

			if stepMain( mw ) = -1 then exit while

			var g_e = new _GUI_EVENT_
			screenlock
			cls
			if custombgfunc <> 0 then
				custombgfunc(custombgdata)
			end if
			g_e->id = gevent.repaint
			SendEvent( mw, g_e )
			screenunlock
			delete g_e

			fps += 1
			/'screenlock
			draw string (1,1), str(int(favg/fnum))
			screensync
			screenunlock'/
			if timer > lastTime then
				lastTime = timer + 1
				favg += fps
				fnum += 1
				fps = 0
				if favg > 50000 then
					favg = favg / fnum
					fnum = 1
				end if
			EndIf
		wend

		delete mw

		ConsolePrint( "Average FPS: " & str(int(favg/fnum)) )

		return err

	end function

	function stepMain( byval mw as BaseWidget ptr = 0 ) as integer

		if screenptr = 0 then return 42

		static w as BaseWidget ptr
		if mw <> 0 then w = mw

		var ret = 0

		static as _GUI_EVENT_ ptr g_e

		_timer_(0)

		g_e = _process_events_()

		if g_e <> 0 then

			if g_e->id = gevent.keystroke then
				if _focus_() <> 0 then
					SendEvent( _focus_(), g_e )
				end if

			else
				ret = SendEvent( mw, g_e )
			EndIf

		else
			g_e = new _GUI_EVENT_
		end if

			if g_e->id = gevent.destroy then

				delete g_e
				return -1

			end if

			delete g_e
			return ret

	End Function

	function _process_events_(  ) as _GUI_EVENT_ ptr

			static as _GUI_EVENT_ ptr g_e
			static as integer cur_m_x, cur_m_y, old_b

			g_e = 0

			if screenevent(0) then
				g_e = new _GUI_EVENT_
				var f_e = new fb.EVENT

				screenevent(f_e)
				select case f_e->type
					case fb.EVENT_KEY_RELEASE
						g_e->ascii = f_e->ascii
						g_e->scancode = f_e->scancode
						g_e->id = gevent.keystroke
					case fb.EVENT_WINDOW_CLOSE
						g_e->id = gevent.destroy
					case fb.EVENT_MOUSE_WHEEL
						g_e->wheel = f_e->z
						g_e->m_x = cur_m_x
						g_e->m_y = cur_m_y
						g_e->id = gevent.wheel
					case fb.EVENT_MOUSE_BUTTON_PRESS
						old_b = f_e->button
						g_e->id = gevent.mousedown
						g_e->m_x = cur_m_x
						g_e->m_y = cur_m_y
						g_e->button = old_b
					case fb.EVENT_MOUSE_MOVE
						g_e->m_x = f_e->x
						g_e->m_y = f_e->y
						g_e->id = gevent.mousemove
						cur_m_x = f_e->x
						cur_m_y = f_e->y

					case fb.EVENT_MOUSE_BUTTON_RELEASE
						if old_b <> 0 then
							if f_e->button = 1 then g_e->id = GEVENT.leftclick
							if f_e->button = 2 then	g_e->id = gevent.rightclick
							if f_e->button = 4 then g_e->id = gevent.middleclick
							old_b = 0
							g_e->m_x = cur_m_x
							g_e->m_y = cur_m_y
							g_e->button = f_e->button
						end if
				End Select
				delete f_e
			end if

		return g_e

	End Function

	sub addTimer( byval widget as BaseWidget ptr, _
		byval time_ as double, byval interval as TIMER_E )

		var x = new uitimer
		x->w = widget
		x->t = time_
		x->i = interval
		_timer_(x)
	end sub

	sub clearFocus ( )

		_focus_(0,0)

	end sub

	private function _focus_( byval w as BaseWidget ptr = 0, byval flag as integer = 0 ) as BaseWidget ptr
		static wdgt as BaseWidget ptr

		if w<>0 then
			wdgt = w
			return 0
		end if

		if flag <> 0 then
			wdgt = 0
			return 0
		end if

		return wdgt

	End Function

	private sub _timer_( byval t as uitimer ptr )

		static list as uitimer_list ptr
		static c_t as double
		static o_t as double

		c_t = timer

		if t = 0 then
			if list = 0 then return
			var tmp = list
			var o_tmp = tmp
			var e = new _GUI_EVENT_
			e->id = GEVENT.time_e
			e->t_e = c_t - o_t
			while 1
				if tmp = 0 then exit while
				if (tmp->_data->t + o_t) < c_t then
					select case tmp->_data->i
						case timer_e.ONESHOT
							tmp->_data->w->process_event( tmp->_data->w, e )
							tmp->_data->i = REMOVE
						case timer_e.REPEAT
							tmp->_data->w->process_event( tmp->_data->w, e )
						case timer_e.ENABLE
							tmp->_data->i = REPEAT
						case timer_e.REMOVE
							var d = tmp
							tmp = d->_next
							o_tmp->_next = tmp
							delete d
					end select
				end if
				o_tmp = tmp
				tmp = tmp->_next
			wend
		else
			var tmp = list
			if tmp = 0 then
				list = new uitimer_list
				list->_data = t
				list->_next = 0
			else
				while tmp->_next <> 0
					tmp = tmp->_next
				wend
				tmp->_next = new uitimer_list
				tmp = tmp->_next
				tmp->_data = t
				tmp->_next = 0
			end if
		end if
		o_t = c_t

	end sub

end namespace
